#define TESTMAIN
#include "Testcase.h"
#include "../Common/Common.h"
#include <stdio.h>
#include <stdlib.h>

#define MAXID 1000

SOCKET g_connectSocket;
SOCKET viceSockClient = INVALID_SOCKET;

char buf[MAXBUFFSIZE];
int assertCnt = 0;
int assertFalse = 0;
char connected[MAXID];
int reqCnt = 0;
HANDLE InvalidViceEvent;
HANDLE gMutex;
int curTestcase;

void assert2(int b, char *sen) {
	++assertCnt;
	if (b==0) {
		printf("assert failed (%d). testcase %d with nodenum %d: %s\n", assertCnt, curTestcase, nodeNum, sen);
		++assertFalse;
		system("pause");
	}
}

void connectAgain(void) {
	SOCKET sock;
	int t = 3600000;;

	WaitForSingleObject(gMutex, INFINITE);

	if (testSocket(g_connectSocket)) {
		setsockopt(g_connectSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&t, sizeof(int));
		ReleaseMutex(gMutex);
		return;
	}

	if (viceSockClient != INVALID_SOCKET) {
		sock = viceSockClient;
		viceSockClient = INVALID_SOCKET;
// 		SetEvent(InvalidViceEvent);
// 		printf("turn to vice.\n");
	}
	else
		sock = createTesterConnectSocket(LOCALHOST);

	g_connectSocket = sock;
	ReleaseMutex(gMutex);
}

int sendReq(int op, int val) {
	int retV;
	int sentTo;


// 	printf("send request: %d %d %d\n", reqCnt, op, val);
	++reqCnt;
	
	sprintf(buf, "%d %d#", op, val);
	while (1) {
		retV = send(g_connectSocket, buf, strlen(buf), 0);
		if (retV < 0) {
			//closesocket(g_connectSocket);
			connectAgain();
			continue;
		}

		retV = recv(g_connectSocket, buf, 4, 0);
// 		printf("in sendReq: %s %d, op=%d val=%d\n", buf, retV, op, val);
		if (retV < 0) {
// 			printf("in SendReq recv error! %d %d\n", retV, GetLastError());
			//closesocket(g_connectSocket);
			connectAgain();
			continue;
		}
		break;
	}

	sscanf(buf, " %d", &sentTo);
// 	printf("send to server ID %d\n", sentTo);

// 	printf("send req return: [len=%d]:%s\n", retV, buf);
// 
// 	sscanf(buf, " %d", &retV);
// 	retV = (retV == YES) ? 1 : 0 ;

	return 1;
}

int getServerData(int op, int val) {
    int ret = -1;
	int recvBytes;
	int t1=0, t2;

	while (1) {
		recvBytes = recv(g_connectSocket, buf, MAXBUFFSIZE, 0);
// 		printf("in GetServerData: [len=%d]%s %d, op=%d val=%d\n", recvBytes, buf, recvBytes, op, val);
		while (recvBytes < 0) {
			t1 = GetLastError();
// 			printf("in GetServerData recv error! %d %d\n", recvBytes, t1);
			//closesocket(g_connectSocket);
			connectAgain();
			if (t1 == 10060) break;
			sendReq(op, val);
			recvBytes = recv(g_connectSocket, buf, MAXBUFFSIZE, 0);
// 			printf("in GetServerData: [len=%d]%s %d, op=%d val=%d\n", recvBytes, buf, recvBytes, op, val);
		}
		if (t1==10060) continue;
		buf[recvBytes] = 0;
		sscanf(buf, "%d %d %d", &t1, &t2, &ret);
		if (t1 == op && t2 == val) break;
		t1=0;
	}
	return ret;
}

void ConnectReq(int userId) {
	connected[userId] = 1;
	if (!sendReq(CONNECT_REQUEST, userId)) {
		printf("connect request failed, maybe there is no PInode!\n");
		system("pause");
	}
}

void ReleaseReq(int userId) {
	if (!sendReq(DELETE_REQUEST, userId)) {
		printf("release request failed, maybe there is no PInode!\n");
		system("pause");
	}
}

int GetPnodeByUser(int userId) {
	if (!sendReq(GETPNODEBYUSER, userId)) {
		printf("get node request failed, maybe there is no PInode!\n");
		system("pause");
		return -1;
	}

	return getServerData(GETPNODEBYUSER, userId);
}

unsigned int GetUserNumByPnode(int nodeId) {
	if (!sendReq(GETUSERNUMBYPNODE, nodeId)) {
		printf("get user number request failed, maybe there is no PInode!\n");
		system("pause");
		return -1;
	}

	return getServerData(GETUSERNUMBYPNODE, nodeId);
}

void StartupPLNode(void) {
	system("start PINode");
	Sleep(2000);
}

void QuitPLNode(int nodeId) {
	if (!sendReq(PINODE_QUIT, nodeId)) {
		printf("quit plnode request failed.\n");
		system("pause");
	}
}

void QuitSysCtrl(void) {
	sendReq(SYSCTRL_QUIT, 0);
	closesocket(g_connectSocket);
}

void StartupSysCtrl(int id, int role, int bossID) {
	char cmd[100];

	sprintf(cmd, "start Controller %d %d %d", id, role, bossID);
	system(cmd);
	if (role == 2)//2 is ROLE_BOSS
		Sleep(1000);
}

void releaseAll(void) {
	int i;
	for (i=0; i<MAXID; ++i)
		if (connected[i] != 0) ReleaseReq(i);
}

void findVice(void *args) {
	SOCKET sock;
	char buf[MAXBUFFSIZE];
	int timeLimit = 1000;
	DWORD waitRes;

	while (1) {
		sock = createConnectSocket(VICE_PORT, LOCALHOST);
		if (sock != INVALID_SOCKET) {
			sprintf(buf, "%d %d#", TESTER, 0);
			send(sock, buf, strlen(buf), 0);
			viceSockClient = sock;
// 			printf("find vice! %d\n",sock);

			while (1) {
				waitRes = WaitForSingleObject(InvalidViceEvent, 1000);
// 				printf("wait res: %d,      %d, %d\n", waitRes, WAIT_OBJECT_0, WAIT_TIMEOUT);
				if (waitRes == WAIT_OBJECT_0) break;
				if (!testSocket(viceSockClient)) {
					viceSockClient = INVALID_SOCKET;
					break;
				}
			}
		}
		Sleep(500);
	}
}

void watchServer(void *args) {
	while (1) {
		if (!testSocket(g_connectSocket)) {
// 			printf("regular test failed!\n");
			connectAgain();
		}
	}
}

int main(int argc, char *argv[]) {
	int i;
	int total = sizeof(testFuns) / sizeof(int);
	int failed = 0;
	int success = 0;
	int *failedNo;
	int choose = 1;
	char order[100];
	int user, pinode;


	failedNo = (int*)malloc(total*sizeof(int));

	startUpNetwork();
	g_connectSocket = createTesterConnectSocket(LOCALHOST);
	InvalidViceEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
	gMutex = CreateMutex(NULL, FALSE, NULL);
	_beginthread(findVice, 0, NULL);
	
    printf("test started\n" );

	if (argc > 1)
		sscanf(argv[1], "%d", &choose);
	else {
		printf("1. human.\n 2. auto.\n");
		scanf("%d", &choose);
// 		choose = 2;
	}
	

	printf("mode select is: %d\n", choose);

	if (choose == 1) {
		_beginthread(watchServer, 0 , NULL);
		while (1) {
			scanf(" %s", order);
			if (strcmp(order, "ConnectReq") == 0) {
				scanf(" %d", &user);
				ConnectReq(user);
			}
			else if (strcmp(order, "ReleaseReq") == 0) {
				scanf(" %d", &user);
				ReleaseReq(user);
			}
			else if (strcmp(order, "GetPnodeByUser") == 0) {
				scanf(" %d", &user);
				pinode = GetPnodeByUser(user);
				printf("the PINode is %d\n", pinode);
			}
			else if (strcmp(order, "GetUserNumByPnode") == 0) {
				scanf(" %d", &pinode);
				user = GetUserNumByPnode(pinode);
				printf("the UserNumber is %d\n", user);
			}
			else if (strcmp(order, "SS") == 0) {
				StartupSysCtrl(-1,-1,-1);
			}
			else if (strcmp(order, "SP") == 0) {
				StartupPLNode();
			}
			else if (strcmp(order, "QS") == 0) {
				QuitSysCtrl();
			}
			else if (strcmp(order, "QP") == 0) {
				scanf(" %d", &pinode);
				QuitPLNode(pinode);
			}
		}
	}
	else if (choose == 2) {

		//normal
		for (nodeNum = 1; nodeNum <= 5; ++nodeNum) {
			printf("change payload node number to %d\n", nodeNum);
			StartupPLNode();
			
			for (i=0; i<total; ++i) {
				printf("Test %d with %d nodes... \n", i+1, nodeNum);
				assertCnt = 0;
				assertFalse = 0;
				curTestcase = i+1;
				memset(connected, 0, MAXID);
				testFuns[i]();
				if (assertFalse > 0) {
					printf("failed.\n");
					failedNo[failed++] = i+1;
				} else {
					printf("ok.\n");
					++success;
				}
				
				releaseAll();
			}
		}

		//no plnode
		printf("Test no plnode... \n");
		assertCnt = 0;
		assertFalse = 0;
		curTestcase = total+1;
		memset(connected, 0, MAXID);
		testcase11();
		if (assertFalse > 0) {
			printf("failed.\n");
			failedNo[failed++] = total+1;
		} else {
			printf("ok.\n");
			++success;
		}
		releaseAll();
		//super
		printf("Test Final Synthesis... \n");
		assertCnt = 0;
		assertFalse = 0;
		curTestcase = total+1;
		memset(connected, 0, MAXID);
		testcase12();
		if (assertFalse > 0) {
			printf("failed.\n");
			failedNo[failed++] = total+1;
		} else {
			printf("ok.\n");
			++success;
		}
		releaseAll();


	}

    printf("test finished\n" );
	printf("\ntotal: %d, success: %d, failed: %d\n", total * (nodeNum-1) + 2, success, failed);
	if (failed>0)
		printf("failed testcase(s) number: ");
	for (i=0; i<failed; ++i)
		printf("%d ", failedNo[i]);
	printf("\n");
		

	printf("Finish all test");
	system("pause");

	return 0;
}
